Contributor(s): Jessica Marx
Date: 12 June 2020
Code: GitHub
Data: Sourced from this Google Sheet and this census data from Kaggle
The following is not meant to be a source of truth in terms of data integrity, but rather to illustrate different ways of displaying, engineering, and/or presenting the data.
# load packages, install if needed
packages = c(
"dplyr"
, "ggplot2"
, "formattable"
, "plotly"
, "RColorBrewer"
, "scales"
, "stringr"
, "tidyr"
, "ElmeR"
, "RJDBC"
, "kableExtra"
, "wesanderson"
, "reshape2"
, "rtweet"
, "tidytext"
, "lubridate"
, "wordcloud"
, "ggpubr"
, "ggthemes"
, "knitrBootstrap"
, "DT"
, "MatchIt"
, "beyonce"
, "UpSetR"
, "gganimate"
, "wordcloud2"
, "widyr"
, "ggraph"
, "igraph"
, "aod"
, "corrplot"
, "ROCR"
, "InformationValue"
, "car"
, "glmnet"
, "caret"
, "kernlab"
, "pdp"
, "rpart.plot"
, "rpart"
, "e1071"
)
package.check <- lapply(packages, FUN = function(x) {
if (!require(x, character.only = TRUE)) {
install.packages(x, dependencies = TRUE)
library(x, character.only = TRUE)
}
})
#import the data
library(readr)
us_county_stats <- read_csv("datasets_579969_1220276_us_county.csv")
brutality_cases <- read_csv("police_brutality.csv")
# glimpse the data
# us_county_stats %>%
# head()
#
# brutality_cases %>%
# head()
Columns and percent of rows with missing fields from Brutality dataset.
#missing values
colMeans(is.na(brutality_cases))
Timestamp
1.0000000000
Date of the Police Brutality
0.0000000000
Approximate Time of the Police Brutality
1.0000000000
State where the Police Brutality Occurred
0.0000000000
City where the Police Brutality Occurred
0.0007829832
Video URL of the Police Brutality
0.4519117839
Names of Victims Involved
0.0000000000
Names of Police Officers Involved
1.0000000000
Badge Numbers of Police Officers involved
1.0000000000
Enter any extra information you want to share here.
1.0000000000
Category
1.0000000000
test
1.0000000000
County
0.0000000000
Category_1
1.0000000000
Summary of US census dataset.
summary(us_county_stats)
fips county state
Min. : 1001 Length:3220 Length:3220
1st Qu.:19032 Class :character Class :character
Median :30024 Mode :character Mode :character
Mean :31394
3rd Qu.:46106
Max. :72153
state_code male female
Length:3220 Min. : 38 Min. : 37
Class :character 1st Qu.: 5658 1st Qu.: 5573
Mode :character Median : 12916 Median : 12996
Mean : 49875 Mean : 51457
3rd Qu.: 33248 3rd Qu.: 33531
Max. :4976788 Max. :5121264
median_age population female_percentage
Min. :21.70 Min. : 75 Min. :21.00
1st Qu.:38.10 1st Qu.: 11214 1st Qu.:49.43
Median :41.20 Median : 25950 Median :50.42
Mean :41.28 Mean : 101332 Mean :49.96
3rd Qu.:44.30 3rd Qu.: 66552 3rd Qu.:51.15
Max. :67.00 Max. :10098052 Max. :58.61
lat long
Min. :17.98 Min. :-164.03
1st Qu.:34.35 1st Qu.: -98.09
Median :38.21 Median : -89.95
Mean :37.97 Mean : -91.64
3rd Qu.:41.69 3rd Qu.: -82.99
Max. :69.31 Max. : -65.29
# Clean dataset (drop nulls)
brutality_clean = brutality_cases
# Remove columns with more than 50% NA
brutality_clean = brutality_clean[, which(colMeans(!is.na(brutality_clean)) > 0.5)]
# Change columns to snake case
snake_case <- function(x) {
colnames(x) <- gsub(" ", "_", colnames(x));x
colnames(x) <-tolower(colnames(x))
}
colnames(brutality_clean) = snake_case(brutality_clean)
brutality_clean = brutality_clean %>%
rename(
"state" = state_where_the_police_brutality_occurred
, "city" = city_where_the_police_brutality_occurred
, "date" = date_of_the_police_brutality
) %>%
mutate(
state_county = paste0(state, "-", county)
)
brutality_clean$date = as.Date(brutality_clean$date, "%m/%d/%Y")
us_stats_clean = us_county_stats
us_stats_clean$county = str_replace(us_stats_clean$county, " County", "")
us_stats_clean = us_stats_clean %>%
group_by(state) %>%
mutate(state_pop = sum(population)) %>%
rename(
"state_full" = state
, "state" = state_code
) %>%
ungroup()
# us_stats_clean =
us_stats_state = us_stats_clean %>%
mutate(state_county = paste0(state, "-", county)) %>%
select(state_pop, state, state_full) %>%
unique()
df_merged = brutality_clean %>%
left_join(us_stats_state) %>%
mutate(state_pop = ifelse(state == "DC", 705749, state_pop)
) %>%
unique()
df_merged = df_merged %>%
group_by(
state
) %>%
add_tally() %>%
ungroup() %>%
rename(
"state_totals" = n
) %>%
mutate(
state_pop_millions = state_pop/1000000
, totals_per_million = state_totals/state_pop_millions
)
Incidents by State
df_plot = df_merged %>%
select(
state, state_pop, state_full, state_pop_millions, state_totals, totals_per_million
) %>%
unique() %>%
arrange(desc(totals_per_million)) %>%
mutate(
state = reorder(as.factor(state), totals_per_million)
)
a = df_plot %>%
plot_ly(
y = ~state
, x = ~totals_per_million
, type = "bar"
, marker = list(color = "#35A7FF")
, orientation = "h"
, width = 900
, height = 1000
, hoverinfo = "text"
, text = ~paste(
"State: ", state_full
, "<br>State Population: ", comma(state_pop)
, "<br>Reported Incidents: ", state_totals
, "<br>Incidents per Million Residents: ", comma(totals_per_million)
)
) %>%
layout(
xaxis = list(title = "Reported Incidents per Million State Residents")
, yaxis = list(title = "")
)
b = df_plot %>%
plot_ly(
y = ~state
, x = ~state_totals
, type = "bar"
, marker = list(color = "#35A7FF")
, orientation = "h"
, width = 900
, height = 1000
, hoverinfo = "text"
, showlegend = FALSE
, text = ~paste(
"State: ", state_full
, "<br>State Population: ", comma(state_pop)
, "<br>Reported Incidents: ", state_totals
, "<br>Incidents per Million Residents: ", comma(totals_per_million)
)
) %>%
layout(
xaxis = list(title = "Total Reported Incidents")
, yaxis = list(title = "")
)
subplot(a, b, titleX = TRUE)
Excluding counties with null populations
There are some counties with null values when it comes to population. We can obviously find these via a better dataset, but for now I’m going to exclude them in order to provide examples with the data that we have. Obviously, this is not accurate reporting.
County Incidents per Capita
df_filtered = df_merged
df_filtered = df_filtered %>%
left_join(us_stats_clean) %>%
filter(!is.na(population))
df_filtered = df_filtered %>%
group_by(state, county) %>%
add_tally() %>%
rename("county_totals" = n) %>%
arrange(desc(county_totals)) %>%
mutate("incidents_per_capita" = county_totals/(population)) %>%
ungroup()
df_county = df_filtered %>%
select(state, state_county, county, state_full, state_totals, median_age, population, county_totals, incidents_per_capita) %>%
unique() %>%
arrange(desc(incidents_per_capita))
df_county %>%
mutate(
incidents_per_capita = comma(incidents_per_capita, .000001)
, population = comma(population)
) %>%
datatable()
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IGZhbHNlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keSB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9Cgp0aCB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMzVBN0ZGOwogICAgY29sb3I6IGJsYWNrOwogICAgZm9udC1zaXplOiAxMHB0OwogICAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgPCEtLSBtYXJnaW4tbGVmdDogYXV0bzsgLS0+CiAgICA8IS0tIG1hcmdpbi1yaWdodDogYXV0bzsgLS0+CiAgICA8IS0tIHBhZGRpbmctdG9wOiAyNXB4OyAtLT4KICB9Cgp0ZCB7ICAvKiBUYWJsZSAgKi8KICBmb250LXNpemU6IDEwcHQ7CiAgPCEtLSB0ZXh0LWFsaWduOiBjZW50ZXI7IC0tPgogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwogIDwhLS0gcGFkZGluZy10b3A6IDI1cHg7IC0tPgp9CgpoMSB7CiAgZm9udC1zaXplOiAxNnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9CiAgCmgyIHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgY29sb3I6ICMxZjc4YjQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7Cn0KCmgzIHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7CiAgfQogIApoNCB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9Cmg1IHsKICBmb250LXNpemU6IDEycHQ7CiAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIHNhbnMtc2VyaWY7Cn0KYSB7CiAgY29sb3I6ICMzNUE3RkY7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwp9CgoKLnNpZGVuYXYgewogIGhlaWdodDogMTAwJTsKICB3aWR0aDogMjAwcHg7CiAgcG9zaXRpb246IGZpeGVkOwogIHotaW5kZXg6IDE7CiAgdG9wOiAwOwogIGxlZnQ6IDA7CiAgYmFja2dyb3VuZC1jb2xvcjogIzM4NjE4QzsKICBvdmVyZmxvdy14OiBoaWRkZW47CiAgcGFkZGluZy10b3A6IDIwcHgKICBwYWRkaW5nLWxlZnQ6IDIwcHg7Cn0KCi5zaWRlbmF2IGEgewogIHBhZGRpbmc6IDZweCA4cHggNnB4IDE2cHg7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogIGZvbnQtc2l6ZTogMTZwdDsKICBmb250LXdlaWdodDogYm9sZGVyOwogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBzYW5zLXNlcmlmOwogIGNvbG9yOiAjRkZGRkZGOwogIGRpc3BsYXk6IGJsb2NrOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKLmNlbnRlciB7CiAgZGlzcGxheTogYmxvY2s7CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgbWFyZ2luLXJpZ2h0OiBhdXRvOwogIHdpZHRoOiAxMDAlOwp9Cgouc2lkZW5hdiBhOmhvdmVyIHsKICBjb2xvcjogI2YxZjFmMTsKfQoKLm1haW4gewogIG1hcmdpbi1sZWZ0OiAyMDBweDsgLyogU2FtZSBhcyB0aGUgd2lkdGggb2YgdGhlIHNpZGVuYXYgKi8KCn0KLm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDE0MDBweDsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87CiAgcGFkZGluZzogMjVweAp9CiAgLypwYWRkaW5nOiAwcHggNXB4OyAqLwp9CgpAbWVkaWEgc2NyZWVuIGFuZCAobWF4LWhlaWdodDogNDUwcHgpIHsKICAuc2lkZW5hdiB7cGFkZGluZy10b3A6IDE1cHg7fQogIC5zaWRlbmF2IGEge2ZvbnQtc2l6ZTogMThweDt9ICAKfQo8L3N0eWxlPgoKPCEtLSBUSVRMRSBJTkZPICAtLT4KCjxkaXYgY2xhc3M9InNpZGVuYXYiPgogIDxpbWcgc3JjPSJncmFzc3Jvb3RzX2xhdy5wbmciIGFsdD0iIiB3aWR0aD0xMDBweCBjbGFzcz0iY2VudGVyIi8+CiAgPGEgaHJlZj0iI2Fib3V0Ij5Qb2xpY2UgQnJ1dGF0bGl0eSBEYXRhIEV4cGxvcmF0aW9uPC9hPgogIDxhIGhyZWY9IiN0b3AiPiA8Zm9udCBmYWNlPSJBcmlhbCIgc2l6ZT0iMiIgY29sb3I9ICIjMzVBN0ZGIj4gdG9wIG9mIHBhZ2UgPC9mb250PjwvYT4KPC9kaXY+Cgo8IS0tIENPTlRFTlQgU1RBUlRTIEhFUkUgIC0tPgoKPGRpdiBjbGFzcz0ibWFpbiI+CjxkaXYgY2xhc3M9ImJvZHkiPgoKIyMgX19Db250cmlidXRvcihzKTpfXyBKZXNzaWNhIE1hcngKIyMgX19EYXRlOl9fIGByIGZvcm1hdChTeXMudGltZSgpLCAiJWQgJUIgJVkiKWAKIyMgX19Db2RlOl9fIDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9temFnYWlub3ZhL3BiLWRhc2hib2FyZC90cmVlL21hc3Rlci93aWRnZXRzIiB0YXJnZXQ9Il9ibGFuayI+R2l0SHViPC9hPgojIyA8Yj5EYXRhOjwvYj4gU291cmNlZCBmcm9tIHRoaXMgPGEgaHJlZj0iaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMTF3TFdNOTU3d2t4Ukl5SzdFRlctU285anFHaHZReGNaODNpcG0xa1ZFblEvZWRpdCNnaWQ9OTE2NDExODA2IiB0YXJnZXQ9Il9ibGFuayI+R29vZ2xlIFNoZWV0PC9hPiBhbmQgdGhpcyBjZW5zdXMgZGF0YSBmcm9tIDxhIGhyZWY9Imh0dHBzOi8vd3d3LmthZ2dsZS5jb20vaGVhZHNvcnRhaWxzL2NvdmlkMTktdXMtY291bnR5LWpodS1kYXRhLWRlbW9ncmFwaGljcz9zZWxlY3Q9dXNfY291bnR5LmNzdiIgdGFyZ2V0PSJfYmxhbmsiPkthZ2dsZTwvYT4KClRoZSBmb2xsb3dpbmcgaXMgbm90IG1lYW50IHRvIGJlIGEgc291cmNlIG9mIHRydXRoIGluIHRlcm1zIG9mIGRhdGEgaW50ZWdyaXR5LCBidXQgcmF0aGVyIHRvIGlsbHVzdHJhdGUgZGlmZmVyZW50IHdheXMgb2YgZGlzcGxheWluZywgZW5naW5lZXJpbmcsIGFuZC9vciBwcmVzZW50aW5nIHRoZSBkYXRhLiAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKCmBgYAoKCmBgYHtyIHBhY2thZ2UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIGxvYWQgcGFja2FnZXMsIGluc3RhbGwgaWYgbmVlZGVkCnBhY2thZ2VzID0gYygKICAgICAgImRwbHlyIgogICAgLCAiZ2dwbG90MiIKICAgICwgImZvcm1hdHRhYmxlIgogICAgLCAicGxvdGx5IgogICAgLCAiUkNvbG9yQnJld2VyIgogICAgLCAic2NhbGVzIgogICAgLCAic3RyaW5nciIKICAgICwgInRpZHlyIgogICAgLCAiRWxtZVIiCiAgICAsICJSSkRCQyIKICAgICwgImthYmxlRXh0cmEiCiAgICAsICJ3ZXNhbmRlcnNvbiIKICAgICwgInJlc2hhcGUyIgogICAgLCAicnR3ZWV0IgogICAgLCAidGlkeXRleHQiCiAgICAsICJsdWJyaWRhdGUiCiAgICAsICJ3b3JkY2xvdWQiCiAgICAsICJnZ3B1YnIiCiAgICAsICJnZ3RoZW1lcyIKICAgICwgImtuaXRyQm9vdHN0cmFwIgogICAgLCAiRFQiCiAgICAsICJNYXRjaEl0IgogICAgLCAiYmV5b25jZSIKICAgICwgIlVwU2V0UiIKICAgICwgImdnYW5pbWF0ZSIKICAgICwgIndvcmRjbG91ZDIiCiAgICAsICJ3aWR5ciIKICAgICwgImdncmFwaCIKICAgICwgImlncmFwaCIKICAgICwgImFvZCIKICAgICwgImNvcnJwbG90IgogICAgLCAiUk9DUiIKICAgICwgIkluZm9ybWF0aW9uVmFsdWUiCiAgICAsICJjYXIiCiAgICAsICJnbG1uZXQiCiAgICAsICJjYXJldCIKICAgICwgImtlcm5sYWIiCiAgICAsICJwZHAiCiAgICAsICJycGFydC5wbG90IgogICAgLCAicnBhcnQiCiAgICAsICJlMTA3MSIKICAgICkKCnBhY2thZ2UuY2hlY2sgPC0gbGFwcGx5KHBhY2thZ2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQogIH0KfSkKYGBgCgoKYGBge3IgZnVuY3Rpb25zLCBlY2hvPUZBTFNFfQoKI2Z1bmN0aW9ucyEKCiNyb3VuZCAKcm91bmQudG8gPC0gZnVuY3Rpb24oeCwgYikgewogIHJvdW5kKHgvYikqYgp9Cgojb2RkcyB0byBwcm9iYWJpbGl0eQpvZGRzLnRvLnByb2IgPC0gZnVuY3Rpb24ob2RkcykgewogIG9kZHMvKDEgKyBvZGRzKSAKfQoKI2xvZyBvZGRzIHRvIHByb2JhYmlsaXR5IApsb2dpdDJwcm9iIDwtIGZ1bmN0aW9uKGxvZ2l0KXsKICBvZGRzIDwtIGV4cChsb2dpdCkKICBwcm9iIDwtIG9kZHMgLyAoMSArIG9kZHMpCiAgcmV0dXJuKHByb2IpCn0KCiNjb252ZXJ0IHRvIGEgcmFuZ2UKcmFuZ2UwMSA8LSBmdW5jdGlvbih4KXsKICAoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKQp9CgojZnVuY3Rpb24gdG8gZ2V0IHZlY3RvciBvZiBjb2xvciB2YWx1ZXMgZnJvbSBSQ29sb3JCcmV3ZXIKZ2V0X2hleF92YWx1ZXMgPC0gZnVuY3Rpb24ocGFsKSB7CiAgYnJld2VyLnBhbChicmV3ZXIucGFsLmluZm9bcGFsLCAibWF4Y29sb3JzIl0sIHBhbCkKfQpwYWlyZWRfY29scyA8LSBnZXRfaGV4X3ZhbHVlcyhwYWwgPSAiUGFpcmVkIikKCiNwYWlyZWQgcGFsZXR0ZSB3aXRoIGJyaWdodGVyIHllbGxvdyAodXNlIHRoaXMgZm9yIGRpdmlzaW9uYWwgY29sb3IgbWFwcGluZykKcGFpcmVkX2JldHRlciA8LSByZXBsYWNlKHBhaXJlZF9jb2xzLCBwYWlyZWRfY29scyA9PSAiI0ZGRkY5OSIsICIjZmVkOTc2IikKCiNyb3VuZCB0byBuZWFyZXN0IDUsIDEwLCBldGMgCnJvdW5kX25lYXJlc3QgPSBmdW5jdGlvbih4LCBiYXNlKSB7CiAgYmFzZSpyb3VuZCh4L2Jhc2UpCn0KCmxlZnQgPSBmdW5jdGlvbih0ZXh0LCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCAxLCBudW1fY2hhcikKfQoKbWlkID0gZnVuY3Rpb24odGV4dCwgc3RhcnRfbnVtLCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCBzdGFydF9udW0sIHN0YXJ0X251bSArIG51bV9jaGFyIC0gMSkKfQoKcmlnaHQgPSBmdW5jdGlvbih0ZXh0LCBudW1fY2hhcikgewogIHN1YnN0cih0ZXh0LCBuY2hhcih0ZXh0KSAtIChudW1fY2hhci0xKSwgbmNoYXIodGV4dCkpCn0KCiN0dXJuIHZhbHVlcyBvdXRzaWRlIG9mIElRUiB0byBOQQpvdXRsaWVycmVwbGFjZW1lbnQgPC0gZnVuY3Rpb24oZGF0YWZyYW1lKXsKICBkYXRhZnJhbWUgJT4lICAgICAgICAgIAogICAgICAgICAgIG1hcF9pZihpcy5udW1lcmljLCB+IHJlcGxhY2UoLngsIC54ICVpbiUgYm94cGxvdC5zdGF0cygueCkkb3V0LCBOQSkpICU+JQogICAgICAgICAgIGRwbHlyOjpiaW5kX2NvbHMoKSAKfQoKI2V2YWx1YXRlIG1vZGVsCmV2YWxfbWV0cmljcyA9IGZ1bmN0aW9uKG1vZGVsLCBkZiwgcHJlZGljdGlvbnMsIHRhcmdldCl7CiAgcmVzaWRzID0gZGZbLHRhcmdldF0gLSBwcmVkaWN0aW9ucwogIHJlc2lkczIgPSByZXNpZHMqKjIKICBOID0gbGVuZ3RoKHByZWRpY3Rpb25zKQogIHIyID0gYXMuY2hhcmFjdGVyKHJvdW5kKHN1bW1hcnkobW9kZWwpJHIuc3F1YXJlZCwgMikpCiAgYWRqX3IyID0gYXMuY2hhcmFjdGVyKHJvdW5kKHN1bW1hcnkobW9kZWwpJGFkai5yLnNxdWFyZWQsIDIpKQogIHByaW50KGFkal9yMikgI0FkanVzdGVkIFItc3F1YXJlZAogIHByaW50KGFzLmNoYXJhY3Rlcihyb3VuZChzcXJ0KHN1bShyZXNpZHMyKS9OKSwgMikpKSAjUk1TRQp9CgpgYGAKCmBgYHtyfQoKI2ltcG9ydCB0aGUgZGF0YQpsaWJyYXJ5KHJlYWRyKQp1c19jb3VudHlfc3RhdHMgPC0gcmVhZF9jc3YoImRhdGFzZXRzXzU3OTk2OV8xMjIwMjc2X3VzX2NvdW50eS5jc3YiKQpicnV0YWxpdHlfY2FzZXMgPC0gcmVhZF9jc3YoInBvbGljZV9icnV0YWxpdHkuY3N2IikKCmBgYAoKCmBgYHtyfQoKIyBnbGltcHNlIHRoZSBkYXRhCiMgdXNfY291bnR5X3N0YXRzICU+JSAKIyAgIGhlYWQoKSAKIyAKIyBicnV0YWxpdHlfY2FzZXMgJT4lIAojICAgaGVhZCgpCgpgYGAKCiMjIENvbHVtbnMgYW5kIHBlcmNlbnQgb2Ygcm93cyB3aXRoIG1pc3NpbmcgZmllbGRzIGZyb20gQnJ1dGFsaXR5IGRhdGFzZXQuIApgYGB7cn0KCiNtaXNzaW5nIHZhbHVlcwpjb2xNZWFucyhpcy5uYShicnV0YWxpdHlfY2FzZXMpKQoKYGBgCgojIyBTdW1tYXJ5IG9mIFVTIGNlbnN1cyBkYXRhc2V0LiAKCmBgYHtyfQoKc3VtbWFyeSh1c19jb3VudHlfc3RhdHMpCgpgYGAKCmBgYHtyfQoKIyBDbGVhbiBkYXRhc2V0IChkcm9wIG51bGxzKQpicnV0YWxpdHlfY2xlYW4gPSBicnV0YWxpdHlfY2FzZXMKCiMgUmVtb3ZlIGNvbHVtbnMgd2l0aCBtb3JlIHRoYW4gNTAlIE5BCmJydXRhbGl0eV9jbGVhbiA9IGJydXRhbGl0eV9jbGVhblssIHdoaWNoKGNvbE1lYW5zKCFpcy5uYShicnV0YWxpdHlfY2xlYW4pKSA+IDAuNSldCgojIENoYW5nZSBjb2x1bW5zIHRvIHNuYWtlIGNhc2UKCnNuYWtlX2Nhc2UgPC0gZnVuY3Rpb24oeCkgewogIGNvbG5hbWVzKHgpIDwtIGdzdWIoIiAiLCAiXyIsIGNvbG5hbWVzKHgpKTt4CiAgY29sbmFtZXMoeCkgPC10b2xvd2VyKGNvbG5hbWVzKHgpKQp9Cgpjb2xuYW1lcyhicnV0YWxpdHlfY2xlYW4pID0gc25ha2VfY2FzZShicnV0YWxpdHlfY2xlYW4pCgpicnV0YWxpdHlfY2xlYW4gPSBicnV0YWxpdHlfY2xlYW4gJT4lIAogIHJlbmFtZSgKICAgInN0YXRlIiA9IHN0YXRlX3doZXJlX3RoZV9wb2xpY2VfYnJ1dGFsaXR5X29jY3VycmVkCiAgICwgImNpdHkiID0gY2l0eV93aGVyZV90aGVfcG9saWNlX2JydXRhbGl0eV9vY2N1cnJlZAogICAsICJkYXRlIiA9IGRhdGVfb2ZfdGhlX3BvbGljZV9icnV0YWxpdHkKICApICU+JSAKICBtdXRhdGUoCiAgICBzdGF0ZV9jb3VudHkgPSBwYXN0ZTAoc3RhdGUsICItIiwgY291bnR5KQogICkKCmJydXRhbGl0eV9jbGVhbiRkYXRlID0gYXMuRGF0ZShicnV0YWxpdHlfY2xlYW4kZGF0ZSwgIiVtLyVkLyVZIikKCmBgYAoKYGBge3J9Cgp1c19zdGF0c19jbGVhbiA9IHVzX2NvdW50eV9zdGF0cwoKdXNfc3RhdHNfY2xlYW4kY291bnR5ID0gc3RyX3JlcGxhY2UodXNfc3RhdHNfY2xlYW4kY291bnR5LCAiIENvdW50eSIsICIiKQoKdXNfc3RhdHNfY2xlYW4gPSB1c19zdGF0c19jbGVhbiAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUpICU+JSAKICBtdXRhdGUoc3RhdGVfcG9wID0gc3VtKHBvcHVsYXRpb24pKSAlPiUgCiAgcmVuYW1lKAogICAgInN0YXRlX2Z1bGwiID0gc3RhdGUKICAgICwgInN0YXRlIiA9IHN0YXRlX2NvZGUKICAgICkgJT4lIAogIHVuZ3JvdXAoKSAKCiMgdXNfc3RhdHNfY2xlYW4gPSAKICAKdXNfc3RhdHNfc3RhdGUgPSB1c19zdGF0c19jbGVhbiAlPiUgCiAgbXV0YXRlKHN0YXRlX2NvdW50eSA9IHBhc3RlMChzdGF0ZSwgIi0iLCBjb3VudHkpKSAlPiUgCiAgc2VsZWN0KHN0YXRlX3BvcCwgc3RhdGUsIHN0YXRlX2Z1bGwpICU+JSAKICB1bmlxdWUoKQoKYGBgCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgpkZl9tZXJnZWQgPSBicnV0YWxpdHlfY2xlYW4gJT4lIAogIGxlZnRfam9pbih1c19zdGF0c19zdGF0ZSkgJT4lIAogIG11dGF0ZShzdGF0ZV9wb3AgPSBpZmVsc2Uoc3RhdGUgPT0gIkRDIiwgNzA1NzQ5LCBzdGF0ZV9wb3ApCiAgKSAlPiUgCiAgdW5pcXVlKCkKCmRmX21lcmdlZCA9IGRmX21lcmdlZCAlPiUgCiAgZ3JvdXBfYnkoCiAgICBzdGF0ZQogICkgJT4lIAogIGFkZF90YWxseSgpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHJlbmFtZSgKICAgICJzdGF0ZV90b3RhbHMiID0gbgogICkgJT4lIAogIG11dGF0ZSgKICAgIHN0YXRlX3BvcF9taWxsaW9ucyA9IHN0YXRlX3BvcC8xMDAwMDAwCiAgICAsIHRvdGFsc19wZXJfbWlsbGlvbiA9IHN0YXRlX3RvdGFscy9zdGF0ZV9wb3BfbWlsbGlvbnMKICApIAoKYGBgCgojIyBJbmNpZGVudHMgYnkgU3RhdGUKCmBgYHtyfQoKZGZfcGxvdCA9IGRmX21lcmdlZCAlPiUgCiAgc2VsZWN0KAogICAgc3RhdGUsIHN0YXRlX3BvcCwgc3RhdGVfZnVsbCwgc3RhdGVfcG9wX21pbGxpb25zLCBzdGF0ZV90b3RhbHMsIHRvdGFsc19wZXJfbWlsbGlvbgogICkgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWxzX3Blcl9taWxsaW9uKSkgJT4lIAogIG11dGF0ZSgKICAgIHN0YXRlID0gcmVvcmRlcihhcy5mYWN0b3Ioc3RhdGUpLCB0b3RhbHNfcGVyX21pbGxpb24pCiAgKSAKCmEgPSBkZl9wbG90ICU+JSAKICBwbG90X2x5KAogICAgeSA9IH5zdGF0ZQogICAgLCB4ID0gfnRvdGFsc19wZXJfbWlsbGlvbgogICAgLCB0eXBlID0gImJhciIKICAgICwgbWFya2VyID0gbGlzdChjb2xvciA9ICIjMzVBN0ZGIikKICAgICwgb3JpZW50YXRpb24gPSAiaCIKICAgICwgd2lkdGggPSA5MDAKICAgICwgaGVpZ2h0ID0gMTAwMAogICAgLCBob3ZlcmluZm8gPSAidGV4dCIKICAgICwgdGV4dCA9IH5wYXN0ZSgKICAgICAgIlN0YXRlOiAiLCBzdGF0ZV9mdWxsCiAgICAgICwgIjxicj5TdGF0ZSBQb3B1bGF0aW9uOiAiLCBjb21tYShzdGF0ZV9wb3ApCiAgICAgICwgIjxicj5SZXBvcnRlZCBJbmNpZGVudHM6ICIsIHN0YXRlX3RvdGFscwogICAgICAsICI8YnI+SW5jaWRlbnRzIHBlciBNaWxsaW9uIFJlc2lkZW50czogIiwgY29tbWEodG90YWxzX3Blcl9taWxsaW9uKQogICAgKQogICkgJT4lIAogIGxheW91dCgKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJSZXBvcnRlZCBJbmNpZGVudHMgcGVyIE1pbGxpb24gU3RhdGUgUmVzaWRlbnRzIikKICAgICwgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpCiAgKQoKYiA9IGRmX3Bsb3QgJT4lIAogIHBsb3RfbHkoCiAgICB5ID0gfnN0YXRlCiAgICAsIHggPSB+c3RhdGVfdG90YWxzCiAgICAsIHR5cGUgPSAiYmFyIgogICAgLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gIiMzNUE3RkYiKQogICAgLCBvcmllbnRhdGlvbiA9ICJoIgogICAgLCB3aWR0aCA9IDkwMAogICAgLCBoZWlnaHQgPSAxMDAwCiAgICAsIGhvdmVyaW5mbyA9ICJ0ZXh0IgogICAgLCBzaG93bGVnZW5kID0gRkFMU0UKICAgICwgdGV4dCA9IH5wYXN0ZSgKICAgICAgIlN0YXRlOiAiLCBzdGF0ZV9mdWxsCiAgICAgICwgIjxicj5TdGF0ZSBQb3B1bGF0aW9uOiAiLCBjb21tYShzdGF0ZV9wb3ApCiAgICAgICwgIjxicj5SZXBvcnRlZCBJbmNpZGVudHM6ICIsIHN0YXRlX3RvdGFscwogICAgICAsICI8YnI+SW5jaWRlbnRzIHBlciBNaWxsaW9uIFJlc2lkZW50czogIiwgY29tbWEodG90YWxzX3Blcl9taWxsaW9uKQogICAgKQogICkgJT4lIAogIGxheW91dCgKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJUb3RhbCBSZXBvcnRlZCBJbmNpZGVudHMiKQogICAgLCB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIikKICApCgpzdWJwbG90KGEsIGIsIHRpdGxlWCA9IFRSVUUpCgpgYGAKCiMjIyBfRXhjbHVkaW5nIGNvdW50aWVzIHdpdGggbnVsbCBwb3B1bGF0aW9uc18KVGhlcmUgYXJlIHNvbWUgY291bnRpZXMgd2l0aCBudWxsIHZhbHVlcyB3aGVuIGl0IGNvbWVzIHRvIHBvcHVsYXRpb24uIFdlIGNhbiBvYnZpb3VzbHkgZmluZCB0aGVzZSB2aWEgYSBiZXR0ZXIgZGF0YXNldCwgYnV0IGZvciBub3cgSSdtIGdvaW5nIHRvIGV4Y2x1ZGUgdGhlbSBpbiBvcmRlciB0byBwcm92aWRlIGV4YW1wbGVzIHdpdGggdGhlIGRhdGEgdGhhdCB3ZSBoYXZlLiBfT2J2aW91c2x5LCB0aGlzIGlzIG5vdCBhY2N1cmF0ZSByZXBvcnRpbmcuXwoKIyMgQ291bnR5IEluY2lkZW50cyBwZXIgQ2FwaXRhIAoKYGBge3J9CgpkZl9maWx0ZXJlZCA9IGRmX21lcmdlZAoKZGZfZmlsdGVyZWQgPSBkZl9maWx0ZXJlZCAlPiUgCiAgbGVmdF9qb2luKHVzX3N0YXRzX2NsZWFuKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShwb3B1bGF0aW9uKSkgCgpkZl9maWx0ZXJlZCA9IGRmX2ZpbHRlcmVkICU+JSAKICBncm91cF9ieShzdGF0ZSwgY291bnR5KSAlPiUgCiAgYWRkX3RhbGx5KCkgJT4lIAogIHJlbmFtZSgiY291bnR5X3RvdGFscyIgPSBuKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50eV90b3RhbHMpKSAlPiUgCiAgbXV0YXRlKCJpbmNpZGVudHNfcGVyX2NhcGl0YSIgPSBjb3VudHlfdG90YWxzLyhwb3B1bGF0aW9uKSkgJT4lIAogIHVuZ3JvdXAoKSAKCmRmX2NvdW50eSA9IGRmX2ZpbHRlcmVkICU+JSAKICBzZWxlY3Qoc3RhdGUsIHN0YXRlX2NvdW50eSwgY291bnR5LCBzdGF0ZV9mdWxsLCBzdGF0ZV90b3RhbHMsIG1lZGlhbl9hZ2UsIHBvcHVsYXRpb24sIGNvdW50eV90b3RhbHMsIGluY2lkZW50c19wZXJfY2FwaXRhKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGFycmFuZ2UoZGVzYyhpbmNpZGVudHNfcGVyX2NhcGl0YSkpCgpkZl9jb3VudHkgJT4lIAogIG11dGF0ZSgKICAgIGluY2lkZW50c19wZXJfY2FwaXRhID0gY29tbWEoaW5jaWRlbnRzX3Blcl9jYXBpdGEsIC4wMDAwMDEpCiAgICAsIHBvcHVsYXRpb24gPSBjb21tYShwb3B1bGF0aW9uKQogICAgKSAlPiUgCiAgZGF0YXRhYmxlKCkKCgpgYGAKCg==